package cn.kevinking.ai.mcp.provider;

import cn.kevinking.ai.mcp.provider.configure.SpringContextHolder;
import cn.kevinking.ai.mcp.provider.model.NativeToolDefinition;
import cn.kevinking.ai.mcp.provider.properties.McpProviderDiscoveryProperties;
import cn.kevinking.ai.mcp.provider.properties.McpProviderProperties;
import cn.kevinking.ai.mcp.provider.service.impl.NamingHttpClient;
import cn.kevinking.ai.mcp.provider.utils.McpToolUtils;
import cn.kevinking.ai.mcp.provider.web.RequestBodyArgumentResolver;
import io.netty.channel.ChannelOption;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.servlet.function.RequestPredicates;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.RouterFunctions;
import org.springframework.web.servlet.function.ServerResponse;
import reactor.netty.http.client.HttpClient;

import java.time.Duration;
import java.util.List;

/**
 * @author kevinking
 */
//@EnableConfigurationProperties(McpProviderProperties.class)
//@ConditionalOnProperty(
//		prefix = McpProviderProperties.CONFIG_PREFIX,
//		name = "enabled",
//		havingValue = "true",
//		matchIfMissing = true
//)
@Configuration
@Slf4j
public class McpProviderAutoConfiguration {

	@Resource
	private Environment env;

	@Resource
	private McpProviderProperties mcpProviderProperties;

	@Resource
	private ServerProperties serverProperties;

	@Bean
//	@ConditionalOnProperty(prefix = McpProviderProperties.CONFIG_PREFIX)
	public McpProvider mcpProvider(List<NativeToolDefinition> tools, SpringContextHolder context) {
		return McpProvider.builder()
				.name(mcpProviderProperties.getName())
				.description(mcpProviderProperties.getDescription())
				.servlet(McpProvider.ProviderServlet.newServlet(serverProperties))
				.tools(McpToolUtils.toToolDefinitionsFromNative(tools, context))
				.build();
	}

	@Bean
	public RouterFunction<ServerResponse> mcpProviderRouters(
			List<NativeToolDefinition> tools,
			RequestBodyArgumentResolver argsResolver,
			SpringContextHolder context
	) {
		List<RouterFunction<ServerResponse>> routers = tools.stream().map(tdf -> {
			final String path = McpToolUtils.generateRouterPath(mcpProviderProperties, env, tdf.getUrl());
			return RouterFunctions.route()
					.POST(path, RequestPredicates.contentType(MediaType.APPLICATION_JSON), request ->
						ServerResponse.ok()
                            .contentType(MediaType.APPLICATION_JSON)
                            .body(argsResolver.resolveArgument(request, context.getApplicationContext().getBean(tdf.getBeanClass()), tdf.getToolMethod())))
					.build();
		}).toList();

		if (!routers.isEmpty()) {
			log.info("[MCP-PROVIDER] Registered {} mcp tools", routers.size());
			return routers.stream().reduce(RouterFunction::and).get();
		}
		return null;
	}

	@Bean
	public WebClient mcpProviderWebClient() {
		HttpClient httpClient = HttpClient.create()
				.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, McpProviderDiscoveryProperties.CONNECTION_TIMEOUT)
				.responseTimeout(Duration.ofMillis(McpProviderDiscoveryProperties.READ_TIMEOUT));
		return WebClient.builder()
				.clientConnector(new ReactorClientHttpConnector(httpClient))
				.build();
	}

	@Bean
	public NamingHttpClient namingHttpAsyncClient(@Qualifier("mcpProviderWebClient") WebClient webClient, McpProviderDiscoveryProperties discoveryProperties, McpProvider mcpProvider) {
		return new NamingHttpClient(webClient, discoveryProperties);
	}
}
